//-------------------------------------------------------------------
//	Copyright (c) 2013-2014  Zhirnov Andrey
//	This file is part of the "UX OpwnGL Framework" project.
//	See copyright notice in "UX_GL_Framework.h".
//-------------------------------------------------------------------
/*
	Files:
		- main file "main.vxl"
			- header
				- version
				- header size
				- byte per voxel
				- voxel format id
				- block size (x, y, z)
				- num blocks (x, y, z)
				- 2d texture size
				- pixel per voxel

		- block file ("block_(<X>,<Y>,<Z>).vxl"
			- header
				- version
				- header size
				- byte per voxel
				- voxel format id
				- block size (x, y, z)
				- data size
			- data (x*y*z*bytePerVoxel)

		- cache "cache_(<X>,<Y>,<Z>).vxl"
			- header
				- version
				- header size
				- byte per voxel
				- voxel format id
				- block size (x, y, z)
				- data size
			- data
				- positive X (y*z)
				- negative X (y*z)
				- positive Y (x*z)
				- negative Y (x*z)
				- positive Z (x*y)
				- negative Z (x*y)

		- mesh "mesh_(<X>,<Y>,<Z>).vxl"
			- header
				- version
				- header size
				- vertex format id
				- num indices
				- num vertices
				- indices data size
				- vertices data size
			- indices
			- vertices
*/

#pragma once

#include "VoxelsBlock.h"
#include "VoxelsBlockCache.h"
#include "VoxelsBlockCacheInBorder.h"
#include "VoxelFileMemCache.h"

namespace Voxels
{

	//
	// Voxel File
	//

	class VoxelFile
	{
	// types
	public:
		enum {
			VERSION			= 1,
			MAX_TEX2D_SIZE	= 8192,	// minimum supported size of 2D image, as OpenCL 1.1 specisfication said
		};

		typedef VoxelsBlock::BlockInfo	BlockInfo;

	private:
#		pragma pack (push, 1)
		struct DefaultHeader
		{
			uint	version;
			uint	headerSize;
			uint	bytePerVoxel;
			uint	voxelFormatId;
			uvec3	blockSize;
			///
			DefaultHeader () :
				version(VERSION), headerSize(0), bytePerVoxel(0), voxelFormatId(0)
			{}
		};

		struct MainHeader : DefaultHeader
		{
			uvec3	numBlocks;
			uint	pixelPerVoxel;
			uvec2	texSize;
			uvec2	numLayers;
			///
			MainHeader () : pixelPerVoxel(0)
			{}
		};

		struct BlockFileHeader : DefaultHeader
		{
			uint64	dataSize;
			///
			BlockFileHeader () : dataSize(0)
			{}
		};
		
		struct CacheFileHeader : DefaultHeader
		{
			uint64	dataSize;
			///
			CacheFileHeader () : dataSize(0)
			{}
		};
#		pragma pack (pop)

		typedef array< VoxelsBlockCache >	voxels_cache_buffer_t;
		
		enum {
			BORDER = Helpers::BORDER
		};


	// variables
	private:
		string						_filePath;

		VoxelFileMemCache			_fileMemCache;
		bool						_useMemCache;
		bool						_borderCacheEnabled;
		
		voxels_cache_buffer_t		_cache;
		VoxelsBlockCacheInBorder	_border;

		MainHeader					_header;
		BlockInfo					_blockWithBorderInfo;


	// methods
	public:
		VoxelFile ();
		~VoxelFile ();

		bool Open (uni_c_string filePath);
		bool Create (uni_c_string filePath, const uvec3 &blockSize, const uvec3 &numBlocks, uint bytePerVoxel, uint voxelFormatId);
		void Close ();

		bool IsExist (uni_c_string filePath) const;

		bool Load (const ivec3 &blockNumber, OUT VoxelsBlockPtr &block);
		bool Load (const ivec3 &blockNumber, OUT VoxelsBlock &block);
		bool Store (const ivec3 &blockNumber, const VoxelsBlock &block);
		bool Flush ();

		void EnableBorderCache (bool enabled);

		uvec3 const &	BlockSize ()		const	{ return _header.blockSize; }
		uvec3 const &	NumBlocks ()		const	{ return _header.numBlocks; }
		uvec3			Dimension ()		const	{ return _header.blockSize * _header.numBlocks; }
		Bytes			BytePerVoxel ()		const	{ return Bytes( _header.bytePerVoxel ); }
		uint			VoxelFormatId ()	const	{ return _header.voxelFormatId; }

		bool			IsOpened ()			const	{ return not _filePath.Empty(); }

		BlockInfo const &	GetBlockWithBorderInfo () const	{ return _blockWithBorderInfo; }

		VoxelsBlockCacheInBorder &	VoxelsInBorder ()	{ return _border; }


	private:
		bool _Init ();

		// blocks //
		bool _LoadBlock (const ivec3 &blockNumber, OUT VoxelsBlockPtr &block);
		bool _CopyCache (const ivec3 &blockNumber, OUT VoxelsBlock &block);
		bool _StoreBlock (const ivec3 &blockNumber, const VoxelsBlock &block);
		bool _CreateCache (const ivec3 &blockNumber, const VoxelsBlock &block);

		// cache //
		bool _LoadCache (const ivec3 &blockNumber);
		bool _StoreCache (const ivec3 &blockNumber);
		bool _UpdateAllCache ();
		bool _CreateAllCache ();
		bool _StoreAllCache ();
		
		VoxelsBlockCache& _GetCache (const ivec3 &blockNumber);

		//bool _CopyBlock (INOUT VoxelsBlock &dstBlock, const VoxelsBlock &srcBlock,
		//				const ivec3 &dstCoord, const ivec3 &srcCoord, const ivec3 &count) const;


		template <typename T>
		bool _CheckHeader (const T &header) const;

		template <typename T>
		void _InitHeader (OUT T &header) const;

		static bool _GetCLImageFormat (uint bytePerVoxel, OUT cl_img_format &format, OUT uint &pixelsPerVoxel);


		static uni_c_string	_FileExt ()				{ return ".vxl"; }

		void _GetMinFileName (OUT string &filename) const;
		void _GetBlockFileName (const ivec3 &blockNumber, OUT string &filename) const;
		void _GetCacheFileName (const ivec3 &blockNumber, OUT string &filename) const;

		uint _BlockNumberToIndex (const ivec3 &blockNumber) const;
	};

	
/*
=================================================
	_CheckHeader
=================================================
*/
	template <typename T>
	inline bool VoxelFile::_CheckHeader (const T &header) const
	{
		return	All( header.blockSize	== _header.blockSize )	and
				header.bytePerVoxel		== _header.bytePerVoxel	and
				header.headerSize		== sizeof(header)		and
				header.version			== _header.version		and
				header.voxelFormatId	== _header.voxelFormatId;
	}
	
/*
=================================================
	_InitHeader
=================================================
*/
	template <typename T>
	inline void VoxelFile::_InitHeader (OUT T &header) const
	{
		header.version			= _header.version;
		header.headerSize		= sizeof(header);
		header.voxelFormatId	= _header.voxelFormatId;
		header.bytePerVoxel		= _header.bytePerVoxel;
		header.blockSize		= _header.blockSize;
	}
	
/*
=================================================
	_BlockNumberToIndex
=================================================
*/
	inline uint VoxelFile::_BlockNumberToIndex (const ivec3 &blockNumber) const
	{
		const uvec3 &	num = NumBlocks();
		const uint		idx = blockNumber.x + blockNumber.y * num.x + blockNumber.z * num.x * num.y;
		return idx;
	}

/*
=================================================
	_GetMinFileName
=================================================
*/
	inline void VoxelFile::_GetMinFileName (OUT string &filename) const
	{
		FileAddressUtils::AddNameToPath( filename, "main" );
		filename << _FileExt();
	}

/*
=================================================
	_GetBlockFileName
=================================================
*/
	inline void VoxelFile::_GetBlockFileName (const ivec3 &blockNumber, OUT string &filename) const
	{
		FileAddressUtils::AddNameToPath( filename, "block_(" );
		filename << blockNumber.x << ',' << blockNumber.y << ',' << blockNumber.z << ')' << _FileExt();
	}
	
/*
=================================================
	_GetCacheFileName
=================================================
*/
	inline void VoxelFile::_GetCacheFileName (const ivec3 &blockNumber, OUT string &filename) const
	{
		FileAddressUtils::AddNameToPath( filename, "cache_(" );
		filename << blockNumber.x << ',' << blockNumber.y << ',' << blockNumber.z << ')' << _FileExt();
	}


}	// Voxels